package utils

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
	"io"
)

var (
	key = []uint8{0x3f, 0x93, 0x1a, 0x67, 0xf0, 0x22, 0xeb, 0x57, 0xac, 0x0e, 0x01, 0x00, 0xbe, 0xea, 0xce, 0x8b}
	iv  = []uint8{0x4b, 0x90, 0x6a, 0x5c, 0x73, 0x0a, 0x45, 0x6a, 0xff, 0xdc, 0x22, 0x8c, 0x17, 0x0b, 0x4f, 0xc8}
)

func Encrypt(clear string) (encrypt string, err error) {
	var (
		block         cipher.Block
		encryptedData []byte
	)
	if block, err = aes.NewCipher(key); err != nil {
		return
	}
	if encryptedData, err = AESCTR(bytes.NewReader([]byte(clear)), block, iv); err != nil {
		return
	}
	encrypt = hex.EncodeToString(encryptedData)
	return
}

func Decrypt(encryptedHexPassword string) (clear string, err error) {
	var (
		encryptedPassword []byte
		block             cipher.Block
	)
	if encryptedPassword, err = hex.DecodeString(encryptedHexPassword); err != nil {
		return
	}

	if block, err = aes.NewCipher(key); err != nil {
		return
	}
	if encryptedPassword, err = AESCTR(bytes.NewReader(encryptedPassword), block, iv); err != nil {
		return
	}
	return string(encryptedPassword), nil
}

func aesctr(value []byte, block cipher.Block, iv []byte) []byte {
	stream := cipher.NewCTR(block, iv)
	ciphertext := make([]byte, len(value))
	stream.XORKeyStream(ciphertext, value)
	return ciphertext
}

func AESCTR(r io.Reader, block cipher.Block, iv []byte) ([]byte, error) {
	var encryptedBytes []byte
	for {
		tmpBytes := make([]byte, block.BlockSize())
		if n, err := r.Read(tmpBytes); err != nil {
			if err == io.EOF {
				break
			} else {
				return nil, fmt.Errorf("failed to read bytes: %s", err)
			}
		} else {
			if n < block.BlockSize() {
				tmpBytes = tmpBytes[:n]
			}
			plaintext := aesctr(tmpBytes, block, iv)
			encryptedBytes = append(encryptedBytes, plaintext...)
		}
	}
	return encryptedBytes, nil
}
